package com.livelab.java;

import com.livelab.model.AssignedExercise;
import com.livelab.model.Exercise;
import com.livelab.util.CommandSynchronized;
import com.livelab.util.CommandSynchronized.Output;
import com.livelab.util.Constant;
import com.livelab.util.MySQLDatabaseUtil;
import com.livelab.util.Utility;
import com.livelab.util.RunResult;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;

/**
 *
 * @author Dakota
 * @author Joey
 * @author Jason
 * 
 */
@ManagedBean
@ViewScoped
public class CompleteExerciseBean {
    /* getters and setters */
    private double totalScore;          //total score compiled
    private String assignedItem;        //selected exercise name
    private String content;             //.java file content
    private Exercise assignment;        //assigned exercise description(test runs)
    private boolean isSet, isExecuted;  //flags
    private File sourceFile, classFile; //File pointers to .java and .class
    private AssignedExercise exercise;  //assigned exercise info(score, due date)
    /* getters */
    private TreeMap<String, String> assigned;
    private String run1, run2, run3;    //test results from .class run
    private MySQLDatabaseUtil db;       //database manager
    private PreparedStatement ps;   
    private String exerciseName;        
    private String instructorUsername;  //student's instructor's username
    private String path;                //path to student's folder
    
    @ManagedProperty(value = "#{studentLoginBean}")
    private StudentLoginBean student;   //student login bean

    /**
     * Constructor
     */
    public CompleteExerciseBean() {
        isSet = false;
        isExecuted = false;
        db = new MySQLDatabaseUtil("localhost/javalivelab9e", "agsuser", "agsuser");

        assigned = new TreeMap<String, String>();
        assignment = new Exercise("", "", false, "", "", "", "", "", "");
    }

    @PostConstruct
    public void init() {
        ResultSet rs;
        //get the instructor associated with course id
        rs = db.executeQuery("SELECT agsinstructor.username AS username FROM agsinstructor, agsstudent WHERE agsstudent.username = '" + student.getUsername() + "' AND agsinstructor.courseID = '" + student.getCourseId() + "';");
        try {
            //if there is an instructor
            if (rs.next()) {
                instructorUsername = rs.getString("username");
                
                //select all exercises assigned by the instructor
                ResultSet temp = db.executeQuery("SELECT exerciseName, dueDate FROM exerciseassigned WHERE username = '" + instructorUsername + "';");
               
                //populate the assignment selection menu
                while (temp.next()) {
                    ResultSet extension = db.executeQuery("SELECT dueDate FROM exercisedueextended WHERE instructorUsername = '" + instructorUsername + "' AND exerciseName = '" + temp.getString("exerciseName") + "' AND studentUsername = '" + student.getUsername() + "';");               
                    //if there is an extention date issued, use it
                    if(extension != null && extension.next()){
                        assigned.put(temp.getString("exerciseName") + " Due: " + extension.getTimestamp("dueDate").toString(), temp.getString("exerciseName"));
                        extension.close();
                    //else, use the default due date
                    }else{
                        assigned.put(temp.getString("exerciseName") + " Due: " + temp.getTimestamp("dueDate").toString(), temp.getString("exerciseName"));
                    }
                }
                temp.close();
            }
            rs.close();
        } catch (SQLException ex) {
            Logger.getLogger(CompleteExerciseBean.class.getName()).log(Level.SEVERE, null, ex);
        }

        //set prepared statement
        ps = db.getPreparedStatement("INSERT INTO agslog(username, exerciseName, comment, score, lastRun) VALUES (?,?,?,?,?);");
        //set the student path

    }

    
    /*
     * GETTERS
     */
    public Exercise getAssignment() {
        return assignment;
    }

    public Map<String, String> getAssignedValue() {
        return assigned;
    }

    public String getAssignedItem() {
        return assignedItem;
    }

    public String getContent() {
        return content;
    }

    public boolean getIsExecuted() {
        return (isExecuted && !student.isNewLogin());
    }

    public boolean getIsSet() {
        return (isSet && !student.isNewLogin());
    }

    public String getRun1() {
        return run1;
    }

    public String getRun2() {
        return run2;
    }

    public String getRun3() {
        return run3;
    }

    public double getScore() {
        return totalScore;
    }

    /*
     * SETTERS
     */
    public void setAssignment(Exercise e) {
        this.assignment = e;
    }

    public void setAssignedItem(String item) {
        this.assignedItem = item;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public void setScore(double totalScore) {
        this.totalScore = totalScore;
    }

    public void setStudent(StudentLoginBean student) {
        this.student = student;
    }

    /*
     * ACTIONS
     */
    
    /**
     * Selecting an exercise from the assigned exercise list
     */
    public void chooseExercise() {
        path = Constant.AGS_ROOT + student.getCourseId() + "/" + student.getUsername() + "/";
        student.setNewLogin(false);
        isExecuted = false;
        FacesContext context = FacesContext.getCurrentInstance();
        
        
        
        //if there was a selection
        if (assignedItem != null && assignedItem.length() > 0) {
            isSet = true;
            exerciseName = assignedItem;
            //set .java file location 
            sourceFile = new File(path + exerciseName + Constant.JAVA_EXT);

            //check if .java file exists
            if (sourceFile.exists()) {
                try {
                    content = new Scanner(sourceFile).useDelimiter("\\A").next();
                } catch (FileNotFoundException ex) {
                    
                }
            //else, populate with a template
            } else {
                content = "/*\n * Name: " + student.getUsername() + "\n"
                        + " */\n\n"
                        + "public class " + exerciseName + "{\n\t"
                        + "public static void main(String[] args){\n\t\t"
                        + "//insert code in this template. Name class " + exerciseName + "\n\t"
                        + "}\n"
                        + "}";
            }

            try {
                //get the selected exercise's decription
                ResultSet rs1;
                rs1 = db.executeQuery("SELECT * FROM  defaultexercise WHERE exerciseName = '" + assignedItem + "';");
                if (rs1.next()) {
                    assignment = new Exercise(rs1.getString("exerciseName"), rs1.getString("exerciseDescription"), rs1.getBoolean("gradable"), rs1.getString("inputA"), rs1.getString("inputB"), rs1.getString("inputC"), rs1.getString("outputA"), rs1.getString("outputB"), rs1.getString("outputC"));
                    context.addMessage(null, new FacesMessage("Populating " + assignedItem, "Complete"));
                }
                rs1.close();

                //get the selected exercise's assignment information (due date, score, etc)
                ResultSet rs2;
                rs2 = db.executeQuery("SELECT * FROM exerciseassigned WHERE exerciseName = '" + assignedItem + "' AND username = '" + instructorUsername + "';");
                ResultSet extension = db.executeQuery("SELECT dueDate FROM exercisedueextended WHERE instructorUsername = '" + instructorUsername + "' AND exerciseName = '" + assignedItem + "' AND studentUsername = '" + student.getUsername() + "';");
                if (rs2.next()) {
                    //if there is an individual extention, use it
                    if(extension != null && extension.next()){
                        exercise = new AssignedExercise(rs2.getString("username"), rs2.getString("exerciseName"), rs2.getTimestamp(3), extension.getTimestamp("dueDate"), rs2.getDouble("score"));
                        extension.close();
                    //else, use the normal due date
                    }else{
                        exercise = new AssignedExercise(rs2.getString("username"), rs2.getString("exerciseName"), rs2.getTimestamp(3), rs2.getTimestamp(4), rs2.getDouble("score"));
                    }
                }
                rs2.close();

                //select previous submition score if one exists
                ResultSet rs3;
                rs3 = db.executeQuery("SELECT score FROM agslog WHERE username='" + student.getUsername() + "' AND exerciseName = '" + exerciseName + "';");
                if (rs3.next()) {
                    totalScore = rs3.getDouble(1);
                } else {
                    totalScore = 0;
                }
                rs3.close();
            } catch (SQLException ex) {
                context.addMessage(null, new FacesMessage("Populating " + assignedItem, "ERROR"));
            }
        } else {
            context.addMessage(null, new FacesMessage("Populating...", "No Selections Made"));
        }
    }

    /**
     * Compile, run, and save the results of the assignment selected
     */
    public void compileSaveRunCode() {
        double score = 0;

        FacesContext context = FacesContext.getCurrentInstance();
        //file's name
        String file = exerciseName + Constant.JAVA_EXT;
        isExecuted = false;

        //reference to the file
        File newFile = new File(path + file);
        
        //read the content input by user to the .java file 
        try {
            Scanner scan = new Scanner(content);
            PrintWriter out = new PrintWriter(newFile);
            while (scan.hasNextLine()) {
                out.println(scan.nextLine());
            }
            out.close();
        } catch (FileNotFoundException ex) {
            context.addMessage("sticky", new FacesMessage("ERROR:", "File not saved!"));
        }

        // compile the program
        Output output = CommandSynchronized.compileProgram(Constant.COMPILE, path, file);
        boolean isCompiled = false;

        //compile success
        if (output.error.length() < 1) {
            context.addMessage(null, new FacesMessage("Compile", "Sucess"));
            isCompiled = true;
            score = 1.0;
        } else {
            score = 0.5;
        }

        if (isCompiled) {
            //run the exercise
            score = runExercise();
        }

        //if the exercise isn't past due
        if (!exercise.isPastDue()) {
            try {

                ResultSet rs4 = db.executeQuery("SELECT exerciseName from agslog WHERE username='" + student.getUsername() + "' AND exerciseName = '" + exerciseName + "';");
                //if the exercise has previously been submitted by the student, update score
                if (rs4.next()) {
                    db.executeStatement("UPDATE agslog SET score = " + score + " WHERE username = '" + student.getUsername() + "' AND exerciseName = '" + rs4.getString("exerciseName") + "';");
                //else, insert the new score into the database
                } else {
                    ps.setString(1, student.getUsername());
                    ps.setString(2, exerciseName);
                    ps.setString(3, "");
                    ps.setDouble(4, score);
                    java.util.Date today = new java.util.Date();
                    ps.setTimestamp(5, new java.sql.Timestamp(today.getTime()));
                    ps.execute();
                }
                rs4.close();
            } catch (SQLException ex) {
                Logger.getLogger(CompleteExerciseBean.class.getName()).log(Level.SEVERE, null, ex);
            }
            //update the accumulated score to display
            totalScore = score;
        }
        
        
    }

    /**
     * Grades an external .class file by comparing output of student's
     * program, given arbitrary input, to a key
     * 
     * @return A score of 1 or the max score the assigned exercise allows
     */
    public double runExercise() {
        isExecuted = true;
        classFile = new File(path + exerciseName + Constant.CLASS_EXT);

        run1 = "";
        run2 = "";
        run3 = "";

        //if the exercise is past due, set a message
        if (exercise.isPastDue()) {
            run1 = "<div id=\"boxstyle\" ; style=\"width: 100%; text-align: left; border-color: blue; margin-left: 0em; margin-right: 0em; margin-top: 0em\"><h3 id=\"h3style\" ; style=\"background-color: blue; color: white;  text-align: left; padding-left: 1em\">Run Status</h3>  <div style = \"margin-left:1em\"><font face=\"Courier New\">";
            run1 += "Past due.";
            return 1.0;
        }

        //if the exercise isn't gradable, set a message
        if (!assignment.getGradable()) {
            run1 = "<div id=\"boxstyle\" ; style=\"width: 100%; text-align: left; border-color: blue; margin-left: 0em; margin-right: 0em; margin-top: 0em\"><h3 id=\"h3style\" ; style=\"background-color: blue; color: white;  text-align: left; padding-left: 1em\">Run Status</h3>  <div style = \"margin-left:1em\"><font face=\"Courier New\">";
            run1 += "Assignment can compile and submit but isn't gradable";
            return 1.0;
        //else, grade the exercise
        } else {
            //path to the first test input
            String inputFileA = Constant.AGS_ROOT + Constant.GRADE_EXERCISE + exerciseName + 'a' + ".input";
            //path to the out file
            String studentOutputFile = path + exerciseName + ".out";

            //if there is no input file
            if (!new File(inputFileA).exists()) {
                //run it with no input
                Output executeProgram = executeAProgram(student.getCourseId(), student.getUsername(), null, exerciseName);
                if (executeProgram.error.equals("error")) {
                    return 1.0;
                } else {
                    //student's output
                    String output = Utility.readFile(studentOutputFile);

                    //read key
                    String keyFile = Constant.AGS_ROOT + Constant.GRADE_EXERCISE + exerciseName + ".output";
                    String instructorOutput = Utility.readFile(keyFile);

                    //compare and grade
                    RunResult run = RunResult.gradeOneRun(output, instructorOutput, 0, executeProgram.timeUsed);
                    run1 = run.outForOneRun;
                    if (run.isCorrect) {
                        return Double.parseDouble(exercise.getScore());
                    } else {
                        return 1.0;
                    }
                }
            //input does exist
            } else {
                //for each potential input file
                for (int i = 'a'; i < 'd'; i++) {
                    //path to the file
                    String file = Constant.AGS_ROOT + Constant.GRADE_EXERCISE + exerciseName + (char) i + ".input";
                   
                    //if the file exists
                    if (new File(file).exists()) {
                        //execute the program
                        Output executeProgram = executeAProgram(student.getCourseId(), student.getUsername(), file, exerciseName);
                        if (executeProgram.error.equals("error")) {
                            return 1.0;
                        } else {
                            //student's output
                            String output = Utility.readFile(studentOutputFile);

                            //read key
                            String keyFile = Constant.AGS_ROOT + Constant.GRADE_EXERCISE + exerciseName + (char)i + ".output";
                                    
                            String instructorOutput = Utility.readFile(keyFile);

                            //compare and grade
                            RunResult run = RunResult.gradeOneRun(output, instructorOutput, 0, executeProgram.timeUsed);
                            switch(i){
                                case 'a':run1 = run.outForOneRun;break;
                                case 'b':run2 = run.outForOneRun;break;
                                case 'c':run3 = run.outForOneRun;break;
                            }
                            if (!run.isCorrect) {
                                return 1.0;
                            }
                        }
                    //input file doesn't exist
                    }else{
                        break;
                    }
                }//end for
                
                //return the max score of the assigned exercise
                return Double.parseDouble(exercise.getScore());
            }
        }
    }

    /**
     * Runs a .class file with an input
     * 
     * @param studentCourseId -- Student's course id
     * @param studentUsername -- Student's username
     * @param inputFilename -- input file name
     * @param exerciseName -- exercise name
     * @return 
     */
    private CommandSynchronized.Output executeAProgram(String studentCourseId, String studentUsername, String inputFilename, String exerciseName) {
        String programDirectory = Constant.AGS_ROOT + studentCourseId + "/" + studentUsername;
        //CommandSynchronized.Output result = CommandSynchronized.runACommand(command, "java5");

        if (inputFilename == null) {
            return CommandSynchronized.executeProgram(Constant.EXECUTE, exerciseName, programDirectory, null, programDirectory + "/" + exerciseName + ".out");
        } else {
            return CommandSynchronized.executeProgram(Constant.EXECUTE, exerciseName, programDirectory, inputFilename, programDirectory + "/" + exerciseName + ".out");
        }
    }

    public static void main(String[] args) {
    }
}